package air

import (
	"encoding/json"
	"net"

	"gitee.com/sdxstar/air/logger"
	"gitee.com/sdxstar/air/protocol"
)

type Context interface {
	Id() string
	IsClosed() bool
	Close()
	Get(key string) interface{}
	Set(key string, value interface{})
	RemoteAddr() net.Addr

	Emit(event string, v interface{}) error
	Ack(event string, v interface{}, r interface{}) error
	BroadcastToAll(event string, msg []byte)
	BroadcastTo(room, event string, msg []byte)
	Join(room string)
	Leave(room string)

	Content() []byte
	Packet() protocol.Packet
	Bind(v interface{}) error

	Reply(v interface{}) error

	Conn() Conn

	Logger() logger.Logger
}

type context struct {
	conn *connection
	p    protocol.Packet
}

func newContext(conn *connection, p protocol.Packet) Context {
	return &context{
		conn: conn,
		p:    p,
	}
}

func (c *context) Reply(v interface{}) error {
	if v == nil {
		rp := c.p.NewReplyPacket(nil)
		return c.conn.reply(rp)
	}
	msg, err := c.conn.srv.opts.Codec.Encode(v)
	if err != nil {
		return err
	}
	rp := c.p.NewReplyPacket(msg)
	return c.conn.reply(rp)
}

func (c *context) Packet() protocol.Packet {
	return c.p
}

func (c *context) Bind(v interface{}) error {
	return json.Unmarshal(c.p.Body(), v)
}

func (c *context) Content() []byte {
	return c.p.Body()
}

func (c *context) Id() string {
	return c.conn.sid
}

func (c *context) IsClosed() bool {
	return c.conn.IsClosed()
}

func (c *context) Close() {
	c.conn.Close()
}

func (c *context) Get(key string) interface{} {
	return c.conn.Get(key)
}

func (c *context) Set(key string, value interface{}) {
	c.conn.Set(key, value)
}

func (c *context) RemoteAddr() net.Addr {
	return c.conn.RemoteAddr()
}

func (c *context) Emit(event string, v interface{}) error {
	return c.conn.Emit(event, v)
}

func (c *context) Ack(event string, v interface{}, r interface{}) error {
	return c.conn.Ack(event, v, r)
}

func (c *context) BroadcastToAll(event string, msg []byte) {
	c.conn.BroadcastToAll(event, msg)
}

func (c *context) BroadcastTo(room, event string, msg []byte) {
	c.conn.BroadcastTo(room, event, msg)
}

func (c *context) Join(room string) {
	c.conn.Join(room)
}

func (c *context) Leave(room string) {
	c.conn.Leave(room)
}

func (c *context) Conn() Conn {
	return c.conn
}

func (c *context) Logger() logger.Logger {
	return c.conn.srv.log
}
